package com.my.geojson.geotools.service.base.impl;


import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.my.geojson.geotools.service.base.SimpleGeoToolService;
import com.my.geojson.store.PostgisDataStore;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.geotools.data.DataStore;
import org.geotools.data.collection.ListFeatureCollection;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.data.simple.SimpleFeatureStore;
import org.geotools.feature.FeatureIterator;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.filter.text.cql2.CQL;
import org.geotools.geometry.jts.JTSFactoryFinder;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.Point;
import org.opengis.feature.Property;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.filter.Filter;
import org.opengis.filter.identity.FeatureId;
import org.springframework.stereotype.Service;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static org.geotools.data.Transaction.AUTO_COMMIT;

@Service
@Slf4j
public class SimpleGeoToolServiceImpl<T>  implements SimpleGeoToolService<T>  {

    @SneakyThrows
    private SimpleFeatureStore getSimpleFeatureStore(Class<T> tClass) {
        //获取表面
        TableName name = tClass.getAnnotation(TableName.class);
        String tableName = name.value();

        DataStore postgisDataStore = PostgisDataStore.getInstance();
        SimpleFeatureSource simpleFeatureSource = postgisDataStore.getFeatureSource(tableName);
        SimpleFeatureStore featureStore = (SimpleFeatureStore) simpleFeatureSource;
        //事务自动提交
        featureStore.setTransaction(AUTO_COMMIT);
        return featureStore;
    }

    @SneakyThrows
    @Override
    public boolean addPointFeature(Double lon, Double lat,Class<T> tClass) {
        TableName name = tClass.getAnnotation(TableName.class);
        String tableName = name.value();

        DataStore postgisDataStore = PostgisDataStore.getInstance();
        SimpleFeatureSource simpleFeatureSource = postgisDataStore.getFeatureSource(tableName);
        SimpleFeatureType type = simpleFeatureSource.getSchema();

        SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(type);

//        Field[] declaredFields = tClass.getDeclaredFields();
//        for (Field field : declaredFields) {
//            boolean aStatic = Modifier.isStatic(field.getModifiers());
//            if(aStatic){
//                continue;
//            }
//            String fieldName = field.getName();
//            if(fieldName.equals("id")){
//                continue;
//            }
//            field.setAccessible(true);
//            Object o = field.get(val);
////            field.set(fieldName, field.get(val));
//            TableField annotation = field.getAnnotation(TableField.class);
//            if(annotation!=null){
//                fieldName = annotation.value();
//            }
//            featureBuilder.set(fieldName,field.get(val));
//        }
////        featureBuilder.add(val);
        GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory(null);
        Point point = geometryFactory.createPoint(new Coordinate(lon,lat));
        //属性值顺序与SimpleFeatureType对应
//        List<Object> resultList = new ArrayList<>();
//        resultList.add(point);
//        resultList.add(5678);
//        resultList.add("erftg");
//        featureBuilder.addAll(resultList);
        featureBuilder.set("name","李四");
        featureBuilder.set("location","重庆市");
        featureBuilder.set("lnglat", Arrays.asList("2.1","2.2","3.3").toArray(new String[3]));
        featureBuilder.set("lnglat_geom",point);
        //虽然有设置默认值，但是不加会报错。。
        featureBuilder.set("is_deleted",0);
        featureBuilder.set("revision",0);
//        featureBuilder.add(point);
        //构建要素
//        SimpleFeature feature = featureBuilder.buildFeature("poi1");
        SimpleFeature feature = featureBuilder.buildFeature(null);

        List<SimpleFeature> features = new ArrayList<>();
        features.add(feature);

        SimpleFeatureCollection featureCollection =
                new ListFeatureCollection(type,features);

        SimpleFeatureStore featureStore = (SimpleFeatureStore) simpleFeatureSource;
        featureStore.setTransaction(AUTO_COMMIT);
        List<FeatureId> added = featureStore.addFeatures(featureCollection);
        System.out.println( "Added "+ added );
        return true;
    }
    @SneakyThrows
    @Override
    public boolean addFeature(Object val, Class<T> tClass) {
        SimpleFeatureStore simpleFeatureStore = getSimpleFeatureStore(tClass);
        SimpleFeatureType type = simpleFeatureStore.getSchema();

        SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(type);
        Field[] declaredFields = tClass.getDeclaredFields();
        for (Field field : declaredFields) {
            boolean aStatic = Modifier.isStatic(field.getModifiers());
            if(aStatic){
                continue;
            }
            String fieldName = field.getName();
            if(fieldName.equals("id")){
                continue;
            }
            field.setAccessible(true);
            TableField annotation = field.getAnnotation(TableField.class);
            if(annotation!=null){
                fieldName = annotation.value();
            }
            featureBuilder.set(fieldName,field.get(val));
        }
        //构建要素
        //SimpleFeature feature = featureBuilder.buildFeature("poi1");
        SimpleFeature feature = featureBuilder.buildFeature(null);

        List<SimpleFeature> features = new ArrayList<>();
        features.add(feature);
        SimpleFeatureCollection featureCollection =
                new ListFeatureCollection(type,features);

        List<FeatureId> featureIds = simpleFeatureStore.addFeatures(featureCollection);
        return true;
    }

    @SneakyThrows
    @Override
    public boolean removeFeatureByName(String name, Class<T> tClass) {
        SimpleFeatureStore simpleFeatureStore = getSimpleFeatureStore(tClass);

        String format = String.format("name = '%s'", name);
        Filter filter = CQL.toFilter(format);
        simpleFeatureStore.removeFeatures(filter);
        return true;
    }
    @SneakyThrows
    @Override
    public ArrayList<T> queryFeature(String filterStr,Class<T> tClass) {
        ArrayList<T> featureList = new ArrayList<T>();

        SimpleFeatureStore simpleFeatureStore = getSimpleFeatureStore(tClass);

        //filterStr形式 如  name='武汉大学' or code like 'tt123%'
        Filter filter = CQL.toFilter(filterStr);
        SimpleFeatureCollection result = simpleFeatureStore.getFeatures(filter);

        //处理数据库属性和类属性不一致的情况
        Field[] declaredFields = tClass.getDeclaredFields();
        List<Field> declaredFieldList = Arrays.asList(declaredFields);
        declaredFieldList = declaredFieldList.stream()
                .filter(x->x.getDeclaredAnnotation(TableField.class)!=null)
                .collect(Collectors.toList());
        Map<String, String> collect = declaredFieldList.stream().collect(
                Collectors.toMap(x -> x.getDeclaredAnnotation(TableField.class).value(),
                        x -> x.getName()));

        FeatureIterator<SimpleFeature> itertor = result.features();
        while (itertor.hasNext()) {
            SimpleFeature feature = itertor.next();
//            featureList.add(feature);
            Geometry geom = (Geometry)feature.getDefaultGeometry();
//                Point centroid = geom.getCentroid();
//            Map<Object, Object> userData = feature.getUserData();
            Collection<Property> properties = feature.getProperties();

            T t = tClass.newInstance();

            Iterator<Property> propertyIterator = properties.iterator();
            while (propertyIterator.hasNext()) {
                Property property = propertyIterator.next();
                Object value = property.getValue();
                String name = property.getName().toString();
                String s = collect.get(name);
                if(StringUtils.isNotBlank(s)){
                    name = s;
                }
                try {
                    //可能没有TableField 注解 所以要这样取一次验证
                    Field field = tClass.getDeclaredField(name);
                    field.setAccessible(true);
                }catch (NoSuchFieldException noSuchFieldException){
                    //没有就不要
                    continue;
                }
                setFieldValueByName(name,t,tClass,value);
            }
            featureList.add(t);
//            String id = feature.getID();
//            List<Object> attributes = feature.getAttributes();
//            log.info("geomStr : "+ geom);
//            log.info("attr ： "+ attributes.toString());
        }
        itertor.close();
        return featureList;
    }
    @SneakyThrows
    @Override
    public boolean modifyFeature(Object val, Class<T> tClass) {
        SimpleFeatureStore simpleFeatureStore = getSimpleFeatureStore(tClass);

        String filterStr=" name = '张三1212' ";
        //修改name为张三1212的name为张大爷
//            String[] names=new String[2];
//            names[0]="StandName";
//            names[1]="the_geom";
//            Object[] values=new Object[2];
//            values[0]=featureName;
//            values[1]=geo;
        String[] names=new String[2];
        names[0]="name";
        Object[] values=new Object[2];
        values[0]="张大爷";
        simpleFeatureStore.modifyFeatures(names, values,
                CQL.toFilter(filterStr));
        return true;
    }


    /**
     * 调用传入的对象的set方法
     *
     * @param fieldName   属性名
     * @param obj         对象实例(XXEntity)
     * @param objectClass 类名(XXEntity.getClass())
     * @param paramObj       设置的属性的值
     * @return
     */

    public static void setFieldValueByName(String fieldName, Object obj, Class objectClass, Object paramObj) {
        try {
            Class[] parameterTypes = new Class[1];
            Field field = objectClass.getDeclaredField(fieldName);
            parameterTypes[0] = field.getType();
            String firstLetter = fieldName.substring(0, 1).toUpperCase();
            String getter = "set" + firstLetter + fieldName.substring(1);
            Method method = objectClass.getMethod(getter, parameterTypes);
            method.setAccessible(true);
            if (field.getType().equals(String.class)) {
                method.invoke(obj, paramObj);
            } else if (field.getType().equals(Integer.class) && !paramObj.equals("")) {
                String param = paramObj.toString();
                method.invoke(obj, Integer.parseInt(param));
            } else if (field.getType().equals(Date.class) && !paramObj.equals("")) {
                String param = paramObj.toString();
                System.out.println("param:" + param);
                if ((param.contains("/") && param.contains(":")) || (param.contains("-") && param.contains(":"))
                        || (param.contains("/") && param.contains(" "))
                        || (param.contains("-") && param.contains(" "))) {
                    if (appearNumber(param, ":") == 1) {
                        method.invoke(obj, sdfH1.parse(param.replaceAll("-", "/")));
                    } else if (appearNumber(param, ":") == 2) {
                        method.invoke(obj, sdfH2.parse(param.replaceAll("-", "/")));
                    } else {
                        method.invoke(obj, sdfH.parse(param.replaceAll("-", "/")));
                    }
                } else if (param.contains("-")) {
                    method.invoke(obj, sdf1.parse(param));
                } else if (param.contains("/")) {
                    method.invoke(obj, sdf2.parse(param));
                } else if (param.contains("年") && param.contains("月") && param.contains("日")) {
                    method.invoke(obj, sdf3.parse(param));
                } else if (param.contains(":")) {
                    method.invoke(obj, sdfTime.parse(param));
                } else {
                    method.invoke(obj, sdf.parse(param));
                }
            }else if (field.getType().equals(String[].class)){
//                List<String> strings = (List<String>) Arrays.asList(paramObj);
//                String[] objects = strings.toArray(new String[strings.size()]);
                method.invoke(obj,paramObj);
            }else if (field.getType().equals(Object.class)){
                method.invoke(obj,paramObj!=null?paramObj.toString():paramObj);
            }else{
                method.invoke(obj,paramObj);
            }
        } catch (IllegalArgumentException e) {
            e.printStackTrace(); //
        } catch (Exception e) {
            e.printStackTrace(); //
        }
    }
    private static SimpleDateFormat sdfH = new SimpleDateFormat("yyyy/MM/dd HH");
    private static SimpleDateFormat sdfH1 = new SimpleDateFormat("yyyy/MM/dd HH:mm");
    private static SimpleDateFormat sdfH2 = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
    private static SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd");
    private static SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy/MM/dd");
    private static SimpleDateFormat sdf3 = new SimpleDateFormat("yyyy/MM/dd");
    private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
    private static SimpleDateFormat sdfTime = new SimpleDateFormat("yyyy/MM/dd");
    /**
     * 获取指定字符串出现的次数
     *
     * @param srcText 源字符串
     * @param findText 要查找的字符串
     * @return
     */
    public static int appearNumber(String srcText, String findText) {
        int count = 0;
        Pattern p = Pattern.compile(findText);
        Matcher m = p.matcher(srcText);
        while (m.find()) {
            count++;
        }
        return count;
    }
}

